package edu.asu.sapa.ground.update;

import edu.asu.sapa.Planner;
import edu.asu.sapa.ground.MathForm;
import edu.asu.sapa.ground.Operator;
import edu.asu.sapa.ground.Problem;
import edu.asu.sapa.ground.PropDB;
import edu.asu.sapa.ground.Proposition;
import edu.asu.sapa.ground.State;
import edu.asu.sapa.heuristic.PG;

public class ConditionChange extends Condition {

	public boolean writeValue;
	
	public ConditionChange(Condition c, boolean writeValue) {
		super(c);
		this.writeValue = writeValue;
	}
	
	public ConditionChange(Proposition p, int id, boolean value, boolean writeValue) {
		this.p = p;
		this.id = id;
		this.value = value;
		this.writeValue = writeValue;
		this.isConstant = false;
	}
	
	public void relaxedUpdate(PG pg, State s, Context c, float time) {
		if (value)
			pg.addGoal(id,time);
		if (writeValue)
			pg.addPendingProposition(p,time+Planner.EPSILON,0,0);
	}
	public void relaxedStart(PG pg, State s, Context c, float time) {
		if (value)
			pg.addGoal(id,time);
	}
	public void relaxedFinish(PG pg, State s, Context c, float time) {
		if (writeValue)
			pg.addPendingProposition(p,time+Planner.EPSILON,0,0);
	}
	
	public boolean updateBefore(State s, Context c, float time) {
		PropDB db = s.propDB;
		return db.readBeforeSetAtInstant(id,time,value,writeValue);
	}
	
	public boolean updateAfter(State s, Context c, float time) {
		return false;
	}
	
	public boolean startBefore(State s, Context c, float time) {
		PropDB db = s.propDB;
		PropDB.Entry e = db.get(id);
		if (!db.readValueBefore(e,time,value))
			return false;
		return db.undefineAt(e,time);
	}
	
	public boolean startAfter(State s, Context c, float time) {
		PropDB db = s.propDB;
		PropDB.Entry e = db.get(id);
		if (!db.readValueAt(e,time,value))
			return false;
		return db.undefineAfter(e,time);
	}

	//see Change
	public boolean finishBefore(State s, Context c, float time) {
		PropDB db = s.propDB;
		return db.unlockSetAtInstant(id,time,writeValue);
	}
	
	public boolean finishAfter(State s, Context c, float time) {
		return false;
	}
	
	@Override
	public boolean applicableBefore(State s, Context c) {
		PropDB db = s.propDB;
		float time = s.time;
		PropDB.Entry e = db.probe(id);
		if (e == null)
			return value == false;
		return e.getValueBefore(time,value) && e.writableAt(time);
	}
	
	@Override
	public boolean applicableAfter(State s, Context c) {
		PropDB db = s.propDB;
		float time = s.time;
		PropDB.Entry e = db.probe(id);
		if (e == null)
			return value == false;
		return e.getValueAt(time,value) && e.writableAfter(time);
	}
	
	public int compareTo(ConditionChange o) {
		//assert this != null && o != null;
		if (id < o.id)
			return -1;
		if (id > o.id)
			return 1;
		if (value != o.value)
			if (!value)
				return -1;
			else
				return 1;
		if (writeValue != o.writeValue)
			if (!writeValue)
				return -1;
			else
				return 1;
		return 0;
	}
	
	public int compareTo(Update u) {
		return compareTo((ConditionChange)u);
	}

	public String toString() {
		return "(-> " + id + " " + value + " " + writeValue + ")";
	}

	public boolean analyzeStatic(float dur) {
		isConstant = false;

		if (p.isConstant) {
			isConstant = true;
			id = 0;
			p = Problem.propositions.get(0);
			if (value == p.value) {
				assert writeValue == value;
				value = true;
				return true;
			} else {
				value = false;
				return false;
			}
		}
		return true;
	}
	
	@Override
	public boolean isFail() {
		return isConstant && !value;
	}
	
	@Override
	public boolean isNoOp() {
		return isConstant && value;
	}
	
	@Override
	public void removeDependencies(Operator o) {
		if (writeValue) {
			p.removeAdd(o.id);
			o.removeAdd(id);
		}
		else {
			p.removeDelete(o.id);
			o.removeDelete(id);
		}
		p.removeCondition(o.id);
		o.removeCondition(id);
		assert value == true;
	}
	
	public void setDependencies(Operator o) {
		if (writeValue) {
			p.addAdd(o.id);
			o.addAdd(id);
		}
		else {
			p.addDelete(o.id);
			o.addDelete(id);
		}
		p.addCondition(o.id);
		o.addCondition(id);
		assert value == true;
	}

}
